package dao.annotations;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;

import dao.hbt.HibernateUtil;
import dao.interfaces.IDAO;
import filtros.Filtro;

public class SDAO<TEntity, TId  extends Serializable, TFilter extends Filtro> implements IDAO {
	
	private static SessionFactory sf;
	private Session session;
	private boolean mustCloseSession;
	
	protected static SessionFactory getSession(){
		if(sf == null){
			sf = HibernateUtil.getSessionFactory();
		}	
		return sf;
	}
	
	public void openSession(){
		session = getSession().openSession();
		mustCloseSession = false;
	}
	
	public void beginTransaction(){
		session.beginTransaction();
	}
	
	public void comitTransaction(){
		session.getTransaction().commit();
	}
	
	public void rollbackTransaction(){
		session.getTransaction().rollback();
	}
	
	public void closeSession(){
		if (session.isConnected()){
			session.close();
		}
		session = null;
		mustCloseSession = false;
	}
	
	public Session obtenerSession(){
		if (session==null){
			openSession();
			mustCloseSession = true;	
		}
		return session;
	}

	public boolean isMustCloseSession() {
		return mustCloseSession;
	}
	
	
	private Class<TEntity> persistentClass;
	
	@SuppressWarnings("unchecked")
	public SDAO(){
		this.persistentClass = (Class<TEntity>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	@SuppressWarnings("unchecked")
	public TEntity obtener(TId id) throws Exception
    {
		Session session = obtenerSession();
		try {
			
			TEntity p = (TEntity) session.get(persistentClass, id);
			return p;
		} catch (Exception e) {
			throw e;
		} finally {
			if (isMustCloseSession())
				closeSession();
		}
    }
	
	@SuppressWarnings("unchecked")
	public TId agregar(TEntity entidad) throws Exception{
		TId id;
		Session session = obtenerSession();

		try {
			if (isMustCloseSession())
				session.beginTransaction();
			
			id = (TId) session.save(entidad);
			session.flush();
			if (isMustCloseSession())
				session.getTransaction().commit();
			return id;

		} catch (Exception e) {
			if (isMustCloseSession())
				session.getTransaction().rollback();
		} finally {
			if (isMustCloseSession())
				closeSession();
		}
		return null;
	}

	public void eliminar(TEntity entidad) {
		Session session = obtenerSession();

		try {
			if (isMustCloseSession())
				session.beginTransaction();
			session.delete(entidad);
			session.flush();
			if (isMustCloseSession())
				session.getTransaction().commit();

		} catch (Exception e) {
			if (isMustCloseSession())
				session.getTransaction().rollback();
		} finally {
			if (isMustCloseSession())
				closeSession();
		}	
	}

	public void grabar(TEntity entidad) {
		Session session = obtenerSession();

		try {
			if (isMustCloseSession())
				session.beginTransaction();				
			session.update(entidad);
			session.flush();
			
			if (isMustCloseSession())
				session.getTransaction().commit();

		} catch (Exception e) {
			if (isMustCloseSession())
				session.getTransaction().rollback();
		} finally {
			if (isMustCloseSession())
				closeSession();
		}
	}

	public int obtenerCantidadRegistros(TFilter filtro) throws Exception
    {
		Session session = obtenerSession();
		try {
			Criteria criteria = session.createCriteria(persistentClass);
			filtro.agregarFiltros(criteria);
			criteria.setProjection(Projections.rowCount());	
			return Integer.parseInt(criteria.uniqueResult().toString());
		} catch (Exception e) {
			throw e;
		} finally {
			if (isMustCloseSession())
				closeSession();
		}
    }

    @SuppressWarnings("unchecked")
	public List<TEntity> listar(TFilter filtro) throws Exception
    {
    	Session session = obtenerSession();
		try {
			Criteria criteria = session.createCriteria(persistentClass);
			if (filtro != null)
            {
				filtro.agregarFiltrosDePaginacion(criteria);
				filtro.agregarFiltros(criteria);
				filtro.agregarOrdenesAlCriterio(criteria);
                criteria.setCacheable(filtro.getCachearConsulta());
            }
			
			return criteria.list();
		} catch (Exception e) {
			throw e;
		} finally {
			if (isMustCloseSession())
				closeSession();
		}
	}
}
